package com.theorydance.esoperator.utils;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 常规方法收集，融合了之前的CommonTools与SystemUtils中的部分方法，并进行了代码优化
 * @author TheoryDance
 */
public class Tools {
	
	/**
	 * 值为“UTF-8”
	 */
	public final static String UTF8 = StandardCharsets.UTF_8.name();

	private Tools() {}
	
	public static Double objToDouble(Object obj) {
		try {
			return obj != null ? Double.parseDouble(obj.toString()) : null;
		} catch (Exception e) {
			return null;
		}
	}
	
	public static double objTodouble(Object obj) {
		try {
			return obj != null ? Double.parseDouble(obj.toString()) : 0.0;
		} catch (Exception e) {
			return 0.0;
		}
	}
	
	public static Long objToLong(Object obj) {
		try {
			return obj != null
					? Long.parseLong(new DecimalFormat("0").format(objToDouble(obj.toString())))
							: null;
		} catch (Exception e) {
			return null;
		}
	}
	
	public static long objTolong(Object obj) {
		try {
			return obj != null
					? Long.parseLong(new DecimalFormat("0").format(objToDouble(obj.toString())))
							: 0L;
		} catch (Exception e) {
			return 0L;
		}
	}
	
	/**
	 * 转换成INT
	 * @param obj
	 * @return
	 */
	public static Integer objToInteger(Object obj) {
		try {
			return obj != null
					? Integer.parseInt(new DecimalFormat("0").format(objToDouble(obj.toString())))
					: null;
		} catch (Exception e) {
			return null;
		}
	}
	/**
	 * 使用案例：<hr>
	 * objToint("-123.6", 3000, x->x>0)
	 * @param object 可以是字符串，可以是数字
	 * @param defaultValue 当不能正常解析的时候，赋值给返回结果
	 * @param constraintFunc 约束条件
	 * @return 当为null、不能正常解析或不能满足约束条件的时候，返回默认值defaultValue
	 */
	public static int objToint(Object object, int defaultValue, Function<Integer, Boolean> constraintFunc) {
		int value = objToint(object, defaultValue);
		
		if(value == defaultValue) {
			return defaultValue;
		}
		
		// 当返回值不为默认值的时候，需要进行约束判断
		if(constraintFunc == null) {
			return value;
		}else {
			if(constraintFunc.apply(value)) {
				return value;
			}
		}
		return defaultValue;
	}
	/**
	 * 将对象转为int
	 * @param object 可以是字符串，可以是数字
	 * @param defaultValue 当不能正常解析的时候，赋值给返回结果
	 * @return 当为null或不能正常解析的时候返回默认值defaultValue
	 */
	public static int objToint(Object object, int defaultValue) {
		
		if(object == null) return defaultValue;
		
		// 如果是数字，就直接返回数字的intValue部分
		if(object instanceof Number) return ((Number)object).intValue();
		
		try {
			double value = Double.parseDouble(object.toString());
			return Double.valueOf(value).intValue();
		} catch (NumberFormatException e) {}
		
		return defaultValue;
    }

	/**
	 * 将对象转为int
	 * @param obj 可以是字符串，可以是数字
	 * @return 当为null或不能正常解析的时候返回0
	 */
	public static int objToint(Object obj) {
		try {
			return obj != null
					? Integer.parseInt(new DecimalFormat("0").format(objToDouble(obj.toString())))
					: 0;
		} catch (Exception e) {
			return 0;
		}
	}
	
	public static String objToStr(Object obj) {
		return objToStr(obj, null);
	}
	
	public static String objToStr(Object obj, String defaultValue) {
		return obj != null ? obj.toString() : defaultValue;
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T objToClass(Object obj, Class<T> clazz) {
		try {
			return (T) (obj==null?null:obj);
		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * 默认使用逗号分隔，如果需要使用其它分隔符，请使用方法`String concat(String separator, Object... objs)`
	 * @param objs
	 * @return
	 */
	@SafeVarargs
	public static <T extends Object> String concat(T... objs) {
		return concatWithSeparator(",", objs);
	}
	
	/**
	 * 
	 * @param wrapper 包裹
	 * @param objs
	 * @return
	 */
	@SafeVarargs
	public static <T extends Object> String concatWithWrapper(String wrapper, T... objs) {
		return concatWithWrapperAndSeparator(",", wrapper, objs);
	}
	
	/**
	 * 根据分隔符，将传递的objs对象作为字符串拼接起来
	 * @param separator 分隔符
	 * @param objs 空字符串视为null处理
	 * @return
	 */
	@SafeVarargs
	public static <T extends Object> String concatWithSeparator(String separator, T... objs) {
		return concatWithWrapperAndSeparator(separator, "", objs);
	}
	
	/**
	 * 根据分隔符和包裹器，将传递的objs对象作为字符串拼接起来
	 * @param separator 分隔符，如：","
	 * @param wrapper 包裹，比如拼装SQL的in条件时，会在字符串两端添加"'"
	 * @param objs
	 * @return
	 */
	@SafeVarargs
	public static <T extends Object> String concatWithWrapperAndSeparator(String separator, final String wrapper, T... objs) {
		return concatWithWrapperAndSeparator(separator, x -> ifnull(wrapper,"")+x.toString()+ifnull(wrapper,""), objs);
	}
	
	/**
	 * 根据分隔符和包裹器，将传递的objs对象作为字符串拼接起来，示例：<br>
	 * concatWithWrapperAndSeparator(",", <font color='red'>x -> "#{" + x.toString() + "}"</font>, "id", "name", "age");<br>
	 * 结果："#{id},#{name},#{age}"
	 * @param separator 分隔符，如：","
	 * @param wrapper 包裹器，可以在两端包裹不一样的信息，极端，在obj对象中间也可以做处理
	 * @param objs
	 * @return
	 */
	public static String concatWithWrapperAndSeparator(String separator, Function<Object, String> wrapper, Object... objs) {
		StringBuffer sb = new StringBuffer();
		
		if(wrapper == null) {
			wrapper = Object::toString;
		}
		for (Object obj : objs) {
			if(obj != null && obj.toString().length() > 0) {
				sb.append(separator + wrapper.apply(obj));
			}
		}
		if(sb.length()>0) {
			return sb.substring(separator.length());
		}
		return sb.toString();
	}
	
	/**
	 * 判断一个对象，是否在数组中，使用对象的toString()进行判断对象是否相同
	 * @param obj
	 * @param array
	 */
	public static boolean in(Object obj, Object[] array) {
		if(array == null) {
			return false;
		}
		if(obj == null) {
			for (Object object : array) {
				if(object == null) {
					return true;
				}
			}
		} else {
			for (Object object : array) {
				if(object != null && obj.toString().equals(object.toString())) {
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * 判断一个对象，是否在集合中，使用对象的toString()进行判断对象是否相同
	 */
	public static boolean in(Object obj, List<? extends Object> list) {
		if(list == null) {
			return false;
		}
		return in(obj, list.toArray());
	}
	
	/**
	 * md5加密
	 * @param text 明文
	 * @return 对应字符串MD5之后的字符串
	 */
	public static String md5(String text) {
        StringBuffer result = new StringBuffer();
        try {
        	// 获取信息摘要对象
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 转换并返回包含16个元素字节数组,返回数值范围为-128到127
            md.update(text.getBytes(UTF8));
            byte[] digest = md.digest();
            // 因为MD5摘要算法得到的结果是128位（即16个字节），而每个字节使用两位的16进制数表示，所以输出 结果是32位输出
            for (int i = 0; i < digest.length; i++) {
            	result.append(Character.forDigit((digest[i] & 0xF0) >> 4, 16));
            	result.append(Character.forDigit((digest[i] & 0xF), 16));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result.toString();
    }
	/**
	 * Secure Hash Algorithm，安全散列算法
	 * @param text
	 * @return
	 */
	public static String sha(String text) {
		 StringBuffer result = new StringBuffer();
		 try {
			 MessageDigest md = MessageDigest.getInstance("SHA");
			 byte[] digest = md.digest(text.getBytes());
			 for (int i = 0; i < digest.length; i++) {
				 //将其中的每个字节转成十六进制字符串：byte类型的数据最高位是符号位，通过和0xff进行与操作，转换为int类型的正整数。
				 String toHexString = Integer.toHexString(digest[i] & 0xff);
				 result.append(toHexString.length() == 1 ? "0" + toHexString : toHexString);
			 }
		 } catch (Exception e) {
			 e.printStackTrace();
		 }
		 return result.toString();
	}
	
	private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";
	
	/**
     * AES加密(对称加密)后，base64编码生成字符串
     * @param content 待加密的内容
     * @param secretKey 密钥（加解密相同），要求16位的字符串
     */
	public static String aesEncryptToStr(String content, String secretKey) throws Exception {
		byte[] bytes = aesEncryptToBytes(content, secretKey);
		return Base64.getEncoder().encodeToString(bytes);
	}
	
	/**
     * AES加密(对称加密)
     * @param content 待加密的内容
     * @param secretKey 密钥（加解密相同），要求16位的字符串
     * @return 加密后的byte[]
     * @throws Exception
     */
    public static byte[] aesEncryptToBytes(String content, String secretKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(secretKey.getBytes(), "AES"));
        return cipher.doFinal(content.getBytes(UTF8));
    }
    
    /**
     * AES解密(对称加密),先将内容进行base64解码，然后再aes解码
     * @param secretKey 密钥（加解密相同），要求16位的字符串
     * @return 解密后的String
     */
    public static String aesDecryptByStr(String encryptStr, String secretKey) throws Exception {
    	byte[] decodeBytes = Base64.getDecoder().decode(encryptStr.getBytes(UTF8));
    	return aesDecryptByBytes(decodeBytes, secretKey);
    }
    
    /**
     * AES解密(对称加密)
     * @param encryptBytes 待解密的byte[]
     * @param secretKey 密钥（加解密相同），要求16位的字符串
     * @return 解密后的String
     * @throws Exception
     */
    public static String aesDecryptByBytes(byte[] encryptBytes, String secretKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKey.getBytes(), "AES"));
        byte[] decryptBytes = cipher.doFinal(encryptBytes);
        return new String(decryptBytes);
    }

	/**
     * 得到UUID，去掉中横线
     * @return
     */
    public static String uuid32(){
    	return uuid36().replace("-", "");
    }
    
    public static String uuid36(){
    	return UUID.randomUUID().toString();
    }
	
	public static boolean isEmpty(String str) {
		return (str==null||str.length()==0) ? true : false;
	}
	
	public static boolean isNotEmpty(String str) {
		return !isEmpty(str);
	}
	
	/**
	 * 判断对象对应的字符串是否为空，等价于：<br>
	 * obj!=null && isNotEmpty(obj.toString())
	 * @param obj
	 * @return
	 */
	public static boolean isNotEmptyStr(Object obj) {
		return obj!=null && isNotEmpty(obj.toString());
	}
	
	/**
	 * <b>在字符串str中，提取${},#{}内的名称</b><hr>
	 * @param str 字符串，示例："hello, <u>${name}</u>, 你好！今日热点信息：<u>#{content}</u>"
	 * @return 提取到的参数，示例返回：["${name}", "#{content}"]
	 */
	public static Set<String> findParams(String str) {
        Set<String> params = new HashSet<>();
        Pattern p = Pattern.compile("([#\\$]\\{.*?\\})");
        Matcher m = p.matcher(str);
        boolean b = m.find();
        while (b) {
            String value = m.group(1);
            params.add(value);
            b = m.find();
        }
        return params;
    }
	
	/**
	 * 判断path路径是否时网络地址，如http://, https://, ftp:// 
	 */
	public static boolean isNetUrl(String path) {
		if(isEmpty(path)){
			return false;
		}
		List<String> prefixList = Arrays.asList("http://", "https://", "ftp://");
		for (String prefix : prefixList) {
			if(path.startsWith(prefix)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 可以使用org.apache.commons.codec.binary.Base64中的编解码
	 */
	public static String base64Encode(String str) {
		try {
			return Base64.getEncoder().encodeToString(str.getBytes(UTF8));
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 可以使用org.apache.commons.codec.binary.Base64中的编解码
	 */
	public static String base64Decode(String str) {
		String ccc = null;
		try {
			byte[] decode = Base64.getDecoder().decode(str.getBytes(UTF8));
			ccc = new String(decode, UTF8);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return ccc;
	}
	
	/**
     * 大陆手机号码11位数，匹配格式：前三位固定格式+后8位任意数
     * 此方法中前三位格式有：
     * 13+任意数
     * 145,147,149
     * 15+除4的任意数(不要写^4，这样的话字母也会被认为是正确的)
     * 166
     * 17+3,5,6,7,8
     * 18+任意数
     * 198,199
     */
    public static boolean isValidPhone(String phone) {
    	/*
    	 * ^ 匹配输入字符串开始的位置
    	 * \d 匹配一个或多个数字，其中 \ 要转义，所以是 \\d
    	 * $ 匹配输入字符串结尾的位置
    	 */
    	String regexp = "^((13[0-9])|(14[5,7,9])|(15[0-3,5-9])|(16[2,5,6])|(17[1,3-8])|(18[0-9])|(19[0-2,6-9]))\\d{8}$";
    	Pattern p = Pattern.compile(regexp);
        Matcher m = p.matcher(phone);
        return m.matches();
    }
    
    /**
     * 求两个时间差
     * @param timeType 时间差的单位
     * @param stime 开始时间
     * @param etime 结束时间
     * @return
     */
    public static long timestampDiff(String timeType, Date stime, Date etime) {
    	long diffSecond = (etime.getTime() - stime.getTime()) / 1000;
    	switch(timeType.toUpperCase()) {
    		case "SECOND":
    			return diffSecond;
    		case "MINUTE":
    			return diffSecond/60;
    		case "HOUR":
    			return diffSecond/60/60;
    		case "DAY":
    			return diffSecond/60/60/24;
    		default:
    			return 0L;
    	}
    }
    
    /**
     * 对时间进行增减
     * @param datetime 说明，需要与format的格式保持一致，该参数可以为null
     * @param format 合法的时间格式
     * @param type Calendar中的常量，如Calendar.MINUTE
     * @param num 增减量，减为负数
     * @return
     */
    public static String time(String datetime, String format, int type, int num) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            Calendar cal = Calendar.getInstance();
            if (isNotEmpty(datetime)) {
                cal.setTime(sdf.parse(datetime));
            }
            cal.add(type, num);
            return sdf.format(cal.getTime()).toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 对时间进行格式化输出
     */
    public static String time(Date date, String format) {
		SimpleDateFormat sdf = new SimpleDateFormat(StrUtil.isEmpty(format) ? "yyyy-MM-dd HH:mm:ss" : format);
		String str = sdf.format(date);
		return str;
	}

	/**
     * 按照输入格式得到当前时间对应的时间字符串
     * @param format 时间格式，例："yyyy-MM-dd"
     */
    public static String time(String format) {
        return time(new Date(), format);
    }
    
    /**
     * 得到当前时间对应的时间字符串，格式"yyyy-MM-dd HH:mm:ss"
     */
    public static String time() {
    	return time("yyyy-MM-dd HH:mm:ss");
    }
    
    /**
	 * 返回时间字符串对应的日期，如果解析错误，则返回当前日期
	 * @param datetimeStr 时间字符串，比如："yyyy-MM-dd HH:mm:ss" 或 "yyyy-MM-dd HH"等对应具体实现
	 * @return
	 */
	public static String date(String datetimeStr) {
		if (isEmpty(datetimeStr)) {
			return today();
		}
		try {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			return sdf.format(sdf.parse(datetimeStr)).toString();
		} catch (Exception e) {}
		return today();
	}

	/**
     * 得到当前时间对应的时间字符串，格式"yyyy-MM-dd HH:mm:ss"
     */
    public static String now() {
    	return time();
    }
    
    public static String hour() {
    	return time("yyyy-MM-dd HH");
    }
    
    public static String hour(int num) {
    	return time(null, "yyyy-MM-dd HH", Calendar.HOUR_OF_DAY, num);
    }
    
    public static String hour(String hour, int num) {
    	return time(hour, "yyyy-MM-dd HH", Calendar.HOUR_OF_DAY, num);
    }
    
    public static String day() {
    	return today();
    }
    
    /**
     * 基于当天日期，进行加减天数
     * @param num 天数
     * @return
     */
    public static String day(int num) {
    	return time(null, "yyyy-MM-dd", Calendar.DAY_OF_MONTH, num);
    }
    /**
     * 基于日期，进行加减天数
     * @param num 天数
     * @return
     */
    public static String day(String day, int num) {
    	return time(day, "yyyy-MM-dd", Calendar.DAY_OF_MONTH, num);
    }
    
    /**
     * 得到今天日期，格式"yyyy-MM-dd"
     */
    public static String today() {
    	return time("yyyy-MM-dd");
    }
    
    /**
     * 得到昨天日期，格式"yyyy-MM-dd"
     */
    public static String yesterday() {
    	return time(today(), "yyyy-MM-dd", Calendar.DAY_OF_MONTH, -1);
    }
    
    /**
     * 得到明天日期，格式"yyyy-MM-dd"
     */
    public static String tomorrow() {
    	return time(today(), "yyyy-MM-dd", Calendar.DAY_OF_MONTH, 1);
    }

	/**
     * 本月字符串
     */
    public static String month() {
    	return month(0);
    }
    
    /**
     * 基于本月，进行加减月数
     * @param num 月数
     */
    public static String month(int num) {
    	return time(null, "yyyy-MM", Calendar.MONTH, num);
    }
    
    /**
     * 基于月，进行加减月数
     * @param num 月数
     */
    public static String month(String month, int num) {
    	return time(month, "yyyy-MM", Calendar.MONTH, num);
    }
    
    /**
     * 本年字符串
     */
    public static String year() {
    	return year(0);
    }
    
    /**
     * 基于本年，进行加减年数
     * @param num 年数
     */
    public static String year(int num) {
    	return time(null, "yyyy", Calendar.YEAR, num);
    }
    
    /**
     * 基于年，进行加减年数
     * @param num 年数
     */
    public static String year(String year, int num) {
    	return time(year, "yyyy", Calendar.YEAR, num);
    }
    
    public static String firstDayOfMonth() {
    	return concatWithSeparator("-", month(), "01");
    }
    public static String firstDayOfMonth(String month) {
    	if(month == null) {
    		return firstDayOfMonth();
    	}
    	return concatWithSeparator("-", month, "01");
    }
    
    public static String firstDayOfYear() {
    	return concatWithSeparator("-", year(), "01", "01");
    }
    
    public static String firstDayOfYear(String year) {
    	if(year == null) {
    		return firstDayOfYear();
    	}
    	return concatWithSeparator("-", year, "01", "01");
    }
    
    public static String lastDayOfMonth(String month) {
    	return concatWithSeparator("-", month, getMonthDay(month));
    }
    
    public static String lastDayOfYear(String year) {
    	return concatWithSeparator("-", year, "12", "31");
    }
    
    public static String timeOperate(String time, String format, int type, int num, int position, int length) {
		String res = null;
		String outFormat = "yyyy-MM-dd HH:mm:ss:SSS";
		try {
			SimpleDateFormat inSdf = new SimpleDateFormat(format);
			SimpleDateFormat outSdf = new SimpleDateFormat(outFormat);
			Calendar c = Calendar.getInstance();
			c.setTime(inSdf.parse(time));
			c.add(type, num);
			res = outSdf.format(c.getTime()).substring(position, length);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return res;
	}

	//获取每个月的天数
    public static String getMonthDay(String date) {
  		String day_31="01,03,05,07,08,10,12";	
  		String day_30="04,06,09,11";	
  		String day=null;
  		if(isNotEmpty(date)) {
  			String[] sp = date.split("-");
  			int year=Integer.valueOf(sp[0]);
  			String month=sp[1];
  			if(day_31.contains(month)){
  				day="31";
  			}else if(day_30.contains(month)){
  				day="30";
  			}else {
  				//1 如果是整百的年份，能被400整除的，是闰年；1.1 如果不是整百的年份，能被4整除的，也是闰年
  				//2 平年28天、闰年29天 
  				if(year%4==0&&year%100!=0||year%400==0){
  					day="29";
  				}else{
  					day="28";
  				}
  			}
  		}else {
  			return null;
  		}
  		return day;
    }

    public static int getTimeDifference(String stime, String etime) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Date date1 = new Date();
        Date date2 = new Date();
        try {
            date1 = format.parse(etime);
            date2 = format.parse(stime);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return (int) ((date1.getTime() - date2.getTime()) / (1000 * 3600 * 24));
    }

    /**
     * 获取某一年的所有月份
     * @param year 年，如: "2016" int 或Str均可
     * @return 这一年内所有月份
     */
    public static List<String> getMonths(Object year) {
        String[] months = {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"};
        List<String> list = Stream.of(months).map(x -> year + "-" + x).collect(Collectors.toList());
        return list;
    }

    /**
     * 返回两个时间之间的集合
     */
    public static List<String> getDates(String type, String sdate, String edate) throws Exception {
        String timeFrom = "";
        Long oneDay = null;
        if (type == "HOUR") {
            timeFrom = "yyyy-MM-dd HH";
            oneDay = 1000 * 60 * 60 * 1l;
        }
        if (type == "DAY") {
            timeFrom = "yyyy-MM-dd";
            oneDay = 1000 * 60 * 60 * 24l;
        }

        DateFormat df_ = new SimpleDateFormat(timeFrom);
        Date startDate = df_.parse(sdate);

        Date endDate = df_.parse(edate);

        Calendar start = Calendar.getInstance();
        start.setTime(startDate);
        Long startTIme = start.getTimeInMillis();

        List<String> list = new ArrayList<>();


        Calendar end = Calendar.getInstance();
        end.setTime(endDate);
        Long endTime = end.getTimeInMillis();


        Long time = startTIme;
        while (time <= endTime) {
            Date d = new Date(time);
            DateFormat df = new SimpleDateFormat(timeFrom);
            list.add(df.format(d));
            time += oneDay;
        }

        return list;
    }
    
    public static String getWeekStartDay() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_WEEK, 1);
        c.add(Calendar.DAY_OF_MONTH, 1);
        return sdf.format(c.getTime());
    }

    /**
     * 判断是否是当前周内的时间
     * @param time
     * @return
     */
    public static boolean isLocalWeek(String time) {
        String stime = getWeekStartDay();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        boolean res = false;
        try {
            res = sdf.parse(time).getTime() >= sdf.parse(stime).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 判断是否是当前月内的时间
     * @param time
     * @return
     */
    public static boolean isLocalMonth(String time) {
        String month = new SimpleDateFormat("yyyy-MM").format(new Date());
        return time.startsWith(month);
    }
    
    /**
     * 计算时间范围内有几周
     *
     * @param sdate
     * @param edate
     * @return
     */
    public static long getWeeksWithTwoDate(String sdate, String edate) {
        long week = LocalDate.parse(sdate).until(LocalDate.parse(edate), ChronoUnit.WEEKS) + 1;
        return week;
    }

    /**
     * 计算时间范围内有几天
     *
     * @param sdate
     * @param edate
     * @return
     */
    public static long getDaysWithTwoDate(String sdate, String edate) {
        long day = LocalDate.parse(sdate).until(LocalDate.parse(edate), ChronoUnit.DAYS) + 1;
        return day;
    }
    
    public final static String[] WIND_DIRS = new String[] { "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S",
			"SSW", "SW", "WSW", "W", "WNW", "NW", "NNW" };

    /**
     * 将风向16方位字符串，转为角度值（0-360°）
     * @param windDir 范围值，例如："NNW"
     * @return
     */
	public static Double windDirToAngle(String windDir) {
		final Double[] angles = new Double[] { 0.0, 22.5, 45.0, 67.5, 90.0, 112.5, 135.0, 157.5, 180.0, 202.5, 225.0,
				247.5, 270.0, 295.5, 315.0, 337.5 };
		for (int i = 0; i < WIND_DIRS.length; i++) {
			if (WIND_DIRS[i].equals(windDir)) {
				return angles[i];
			}
		}
		return 0.0;
	}

	public static String getWindDescriptByDir(String dir) {
		final String[] descripts = new String[] { "北", "东北东", "东北", "东东北", "东", "东东南", "东南", "南东南", "南", "南西南", "西南",
				"西西南", "西", "西西北", "西北", "北西北" };
		for (int i = 0; i < WIND_DIRS.length; i++) {
			String temp = WIND_DIRS[i];
			if (temp.equalsIgnoreCase(dir)) {
				return descripts[i];
			}
		}
		return dir;
	}

	public static String getWindDirByAngle(Double angle) {
		String direction = "不详";
		if (angle == null) {
			return direction;
		}
		// 将角度转为文字描述的风向
		if (angle <= 11.25) {
			direction = "北";
		} else if (angle <= 22.5 + 11.25) {
			direction = "北东北";
		} else if (angle <= 45.0 + 11.25) {
			direction = "东北";
		} else if (angle <= 67.5 + 11.25) {
			direction = "东东北";
		} else if (angle <= 90.0 + 11.25) {
			direction = "东";
		} else if (angle <= 112.5 + 11.25) {
			direction = "东东南";
		} else if (angle <= 135 + 11.25) {
			direction = "东南";
		} else if (angle <= 157.5 + 11.25) {
			direction = "南东南";
		} else if (angle <= 180 + 11.25) {
			direction = "南";
		} else if (angle <= 202.5 + 11.25) {
			direction = "南西南";
		} else if (angle <= 225 + 11.25) {
			direction = "西南";
		} else if (angle <= 247.5 + 11.25) {
			direction = "西西南";
		} else if (angle <= 270 + 11.25) {
			direction = "西";
		} else if (angle <= 292.5 + 11.25) {
			direction = "西西北";
		} else if (angle <= 315 + 11.25) {
			direction = "西北";
		} else if (angle <= 337.5 + 11.25) {
			direction = "北西北";
		} else {
			direction = "北";
		}
		return direction;
	}

	/**
	 * 将src中的attr对应的值，拷贝到des对象中
	 * @param src 源，不能为null
	 * @param des 目标对象，不能为null
	 * @param attrs 属性名称， 不能为null
	 */
	public static <T> void copyAttr(Map<String, T> src,Map<String, T> des,String... attrs) {
		for (String attr : attrs) {
			des.put(attr, src.get(attr));
		}
	}
	/**
	 * 读取文本流中的文本信息
	 * @param input 文本输入流
	 * @return
	 */
	public static String readText(InputStream input) {
		BufferedReader br = null;
		StringBuffer sb = new StringBuffer();
		try {
			br = new BufferedReader(new InputStreamReader(input)); 
			try {
				String line = null;
				while((line=br.readLine())!=null) {
					sb.append(line + "\n");
				}
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}catch (Exception e) {
			closeIO(br);
		}
		return sb.toString();
	}
	
	/**
	 * 
	 * @param localFilePath 本地文件路径
	 * @return
	 */
	public static String readText(String localFilePath) {
		InputStream input = null;;
		try {
			input = new FileInputStream(new File(localFilePath));
			return readText(input);
		} catch (FileNotFoundException e) {
			return null;
		} finally {
			closeIO(input);
		}
	}
	
	/**
	 * 关闭流等操作
	 * @param ioStream 需要实现Closeable接口的对象
	 */
	public static void closeIO(Closeable ioStream) {
        if (ioStream != null) {
            try {
            	ioStream.close();
            } catch (IOException e) {
            }
        }
    }
	
	private final static int DEFAULT_HTTP_CONNECT_TIMEOUT = 1000;
	private final static int DEFAULT_HTTP_READ_TIMEOUT = 3000;

	/**
	 * 这里使用原始的http请求连接，没有使用http相关的工具包（比如hutool），是因为在基础模块中可能没有相关依赖，直接使用java自带的
	 * @param fullUrl 完整网络请求路径，包含协议头
	 * @param body	post发送的body内容体
	 * @param contentType 请求头"Content-Type"内容，比如:"application/json"
	 * @param connTimeout 连接超时时间
	 * @param readTimeout 读取超时时间
	 * @return 响应body内容
	 */
    public static String httpPost(String fullUrl, String body, String contentType, int connTimeout, int readTimeout) {
        HttpURLConnection conn = null;
        OutputStream out = null;
        InputStream input = null;
        ByteArrayOutputStream outputs = null;
        try {
            conn = (HttpURLConnection) new URL(fullUrl).openConnection();
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setConnectTimeout(connTimeout>0?connTimeout:DEFAULT_HTTP_CONNECT_TIMEOUT);
            conn.setReadTimeout(readTimeout>0?readTimeout:DEFAULT_HTTP_CONNECT_TIMEOUT);
            conn.addRequestProperty("Content-Type", contentType);
            out = conn.getOutputStream();
            out.write(body.getBytes());
            out.flush();
            input = conn.getInputStream();
            
            int size = 1024*5;
            int len=0;
            outputs = new ByteArrayOutputStream();
            StringBuffer content = new StringBuffer(size);
            byte[] buf = new byte[size];
            while((len=input.read(buf))!=-1) {
                outputs.write(buf, 0, len);
            }
            
            content.append(new String(outputs.toByteArray()));
            return content.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        	Tools.closeIO(out);
            Tools.closeIO(input);
            Tools.closeIO(outputs);
        }
        return null;
    }
    
    /**
	 * 发送post请求
	 * @param fullUrl 完整网络请求路径，包含协议头
	 * @param body	post发送的body内容体
	 * @param contentType 请求头"Content-Type"内容，比如:"application/json"
	 * @return 响应body内容
	 */
    public static String httpPost(String fullUrl, String body, String contentType) {
    	return httpPost(fullUrl, body, contentType, DEFAULT_HTTP_CONNECT_TIMEOUT, DEFAULT_HTTP_READ_TIMEOUT);
    }
    
    /**
	 * 发送application/json的post请求
	 * @param fullUrl 完整网络请求路径，包含协议头
	 * @param body	post发送的body内容体
	 * @return 响应body内容
	 */
    public static String httpPostJSON(String fullUrl, String body) {
    	return httpPost(fullUrl, body, MimeType.JSON.getCode(), DEFAULT_HTTP_CONNECT_TIMEOUT, DEFAULT_HTTP_READ_TIMEOUT);
    }
    
    /**
	 * 发送application/json的post请求
	 * @param fullUrl 完整网络请求路径，包含协议头
	 * @param body	post发送的body内容体
	 * @param connTimeout 连接超时时间
	 * @param readTimeout 读取超时时间
	 * @return 响应body内容
	 */
    public static String httpPostJSON(String fullUrl, String body, int connTimeout, int readTimeout) {
    	return httpPost(fullUrl, body, MimeType.JSON.getCode(), connTimeout, readTimeout);
    }
    /**
     * 将参数params中的数据拼接到请求地址上
     */
	public static String httpGet(String httpUrl, Map<String,String> params){
		if(params == null || params.size() == 0) {
			return httpGet(httpUrl);
		}
		String paramstr = "";
		for (String key : params.keySet()) {
			paramstr += "&" + key + "=" + params.get(key);
		}
		if(httpUrl.contains("?")) {
			return httpGet(httpUrl + paramstr);
		}else {
			return httpGet(httpUrl + "?" + paramstr.substring(1));
		}
	}
	
	public static String httpGet(String httpUrl){
		return httpGet(httpUrl, DEFAULT_HTTP_CONNECT_TIMEOUT, DEFAULT_HTTP_READ_TIMEOUT);
	}
	
	public static String httpGet(String httpUrl,int connectTimeout,int readTime){
		try {
			URL url = new URL(httpUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(connectTimeout);
			conn.setReadTimeout(readTime);
			conn.connect();
			InputStream input = conn.getInputStream();
			int len = 0;
			byte[] buf = new byte[1024];
			StringBuffer sb = new StringBuffer();
			while((len=input.read(buf))!=-1) {
				sb.append(new String(buf, 0, len, UTF8));
			}
			input.close();
			return sb.toString();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 加法
	 */
	public static BigDecimal calcAdd(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2);
    }

	/**
	 * 减法
	 */
    public static BigDecimal calcSub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2);
    }

    /**
	 * 乘法
	 */
    public static BigDecimal calcMul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2);
    }

    /**
     * v1/v2的值，四舍六入5留双，默认无小数
     */
    public static BigDecimal calcDiv(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, 0, BigDecimal.ROUND_HALF_EVEN);
    }

    /**
     * 返回v1/v2的值、四舍六入5留双、指定小数位数
     */
    public static BigDecimal calcDivScale(double v1, double v2, int scale) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_EVEN);
    }

    /**
	 * 提供精确的小数位四舍五入处理。
	 *
	 * @param v     需要四舍五入的数字
	 * @param scale 小数点后保留几位
	 * @return 四舍五入后的结果
	 */
	public static double calcRound(double v, int scale) {
	    if (scale < 0) {
	        throw new IllegalArgumentException("The scale must be a positive integer or zero");
	    }
	    BigDecimal b = new BigDecimal(Double.toString(v));
	    BigDecimal one = new BigDecimal("1");
	    return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 提供精确的格式化小数位四舍五入处理。
	 *
	 * @param v      需要四舍五入的数字
	 * @param scale  小数点后保留几位
	 * @param Format 输出的格式 如保留小数点后2位"#.00"
	 * @return 四舍五入后的格式化结果
	 */
	public static String calcRound(double v, int scale, String Format) {
	    if (scale < 0) {
	        throw new IllegalArgumentException("The scale must be a positive integer or zero");
	    }
	    BigDecimal b = new BigDecimal(Double.toString(v));
	    BigDecimal one = new BigDecimal("1");
	    DecimalFormat df1 = new DecimalFormat(Format);
	    return df1.format(b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue());
	}

	/**
	 * 提供精确的格式化小数位处理。
	 *
	 * @param v      需要四舍五入的数字
	 * @param Format 输出的格式 如保留小数点后2位"#.00"
	 * @return 四舍五入后的格式化结果
	 */
	public static String calcRound(double v, String Format) {
	    DecimalFormat df1 = new DecimalFormat(Format);
	    return df1.format(v);
	}

	/**
	 * 获取小数后进行截取位数
	 *
	 * @param val 值
	 * @param num 小数点后保留的位数
	 * @return
	 */
	public static double calcDecimal(Double val, int num) {
	    if (val == null) {
	        return 0;
	    }
	    BigDecimal b = new BigDecimal(val);
	    return b.setScale(num, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * @param value 需要科学计算的数据
	 * @param digit 保留的小数位
	 * @return 功能：四舍六入五成双计算法
	 */
	public static double calcSciCal(double value, int digit) {
	    String result = "-999";
	    try {
	        double ratio = Math.pow(10, digit);
	        double _num = value * ratio;
	        double mod = _num % 1;
	        double integer = Math.floor(_num);
	        double returnNum;
	        if (mod > 0.5) {
	            returnNum = (integer + 1) / ratio;
	        } else if (mod < 0.5) {
	            returnNum = integer / ratio;
	        } else {
	            returnNum = (integer % 2 == 0 ? integer : integer + 1) / ratio;
	        }
	        BigDecimal bg = new BigDecimal(returnNum);
	        result = bg.setScale((int) digit, BigDecimal.ROUND_HALF_UP).toString();
	    } catch (RuntimeException e) {
	        throw e;
	    }
	    double dou = Double.parseDouble(result);
	    return dou;
	}
    @Getter
    @AllArgsConstructor
    public enum MimeType {
    	
    	JSON("application/json");
    	
    	private String code;
    }
    
    public static <T> T ifnull(T obj, T defaultValue) {
    	return obj!=null?obj:defaultValue;
    }
    /**
     * 递归删除目录
     * @param dir 目录
     * @throws IOException
     */
    public static void deleteDirectory(File dir) throws IOException {
	    if ((dir == null) || !dir.isDirectory()) {
	        return;
	    }
	    File[] entries = dir.listFiles();
	    int sz = entries.length;
	    for (int i = 0; i < sz; i++) {
	        if (entries[i].isDirectory()) {
	        	deleteDirectory(entries[i]);
	        } else {
	            entries[i].delete();
	        }
	    }
	    dir.delete();
	}

    /**
     * Number从0开始
     * @param template 模板，里面含有参数{Number},表示取后面objs[Number]对应的参数，如果取不到，对应的值为null
     * @param objs
     * @return
     */
	public static String format(String template, Object... objs) {
		int objsSize = objs.length;
		String result = template;
        Pattern p = Pattern.compile("\\{(\\d*?)\\}");
        Matcher m = p.matcher(template);
        boolean b = m.find();
        while (b) {
            String value = m.group(1);
            if(objToint(value) >= objsSize){
            	result = result.replace("{"+value+"}", "null");
            }else {
            	result = result.replace("{"+value+"}", objToStr(objs[objToint(value)], "null"));
            }
            b = m.find();
        }
		return result;
	}
	
	/**
	 * 序列化方式深拷贝对象，其它实现，可以将对象json化，利用json相关进行拷贝<br>
	 * com.alibaba.fastjson.JSON深拷贝实现<br><font color='red'>
	 * T deepCopyByJson(T obj) {<br>
	 * &emsp;&emsp;String json = JSON.toJSONString(obj);<br>
	 * &emsp;&emsp;return (T) JSON.parseObject(json, obj.getClass());<br>
	 * }</font>
	 * @param obj 要求实现了接口Serializable
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Serializable> T copyByDeep(T obj) {
		T cloneObj = null;
	    ObjectOutputStream oos = null;
	    ObjectInputStream ois = null;
	    try {
	        ByteArrayOutputStream baos = new ByteArrayOutputStream();
	        oos = new ObjectOutputStream(baos);
	        oos.writeObject(obj);

	        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
	        ois = new ObjectInputStream(bais);
	        cloneObj = (T) ois.readObject();
	    } catch (Exception e) {
	        e.printStackTrace();
	    } finally {
	    	closeIO(oos);
            closeIO(ois);
	    }
	    return cloneObj;
	}

	/**
	 * 获取异常的详细输出
	 */
	public static String getExceptionStr(Exception e) {
		if(e == null) {
			return "";
		}
		StringBuffer sb = new StringBuffer();
		sb.append("message: "+e.getMessage() + "\n");
		StackTraceElement[] elements = e.getStackTrace();
		for (int i = 0; i < elements.length; i++) {
			StackTraceElement element = elements[i];
			sb.append("stackTrace line:" + i + " : "+element.toString() + "\n");
		}
		return sb.toString();
	}
	
	/**
	 * @param wrapper 两边包裹
	 * @param value
	 * @return
	 */
	public static String wrapper(String wrapper, Object value) {
		return value==null?null:(wrapper + value.toString() + wrapper);
	}
	
	/**
	 * 进行多次尝试，比如网络服务，需要进行失败重试机制，默认0.5s，3次，如果要对间隔时间和次数进行控制，使用另一个参数方法
	 */
	public static <T> T tryDo(SupplierWithException<T> supplier) {
		return tryDo(supplier, 500, 3);
	}
	/**
	 * 进行多次尝试，比如网络服务，需要进行失败重试机制
	 * @param supplier
	 * @param intervalMs 间隔毫秒数
	 * @param times	重试次数
	 * @return 如果正常，就返回数据，重试一直失败，就返回null
	 */
	public static <T> T tryDo(SupplierWithException<T> supplier, int intervalMs, int times) {
		T t = null;
		int count = 0;
		do {
			try {
				// 一般重试的是网络情况，会抛出异常
				t = supplier.get();
				break;
			}catch(Exception e) {}
		}while(t == null && ++count < times);
		return t;
	}
	
	/**
	 * 判断两个对象的字符串是否相等
	 */
	public static boolean equalsStr(Object obj1, Object obj2){
		if(isAnyNull(obj1, obj2)) {
			return false;
		}
		return obj1.toString().equals(obj2.toString());
	}
	
	/**
	 * 有一个为null，返回true，否则返回false
	 */
	public static boolean isAnyNull(Object... objects) {
		return anyIsNull(objects);
	}
	
	/**
	 * 有一个为null，返回true，否则返回false
	 */
	public static boolean anyIsNull(Object... objects) {
		for (Object object : objects) {
			if(object == null) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 有一个不为null，返回true，否则返回false
	 */
	public static boolean anyIsNotNull(Object... objects) {
		for (Object object : objects) {
			if(object != null) {
				return true;
			}
		}
		return false;
	}
	
	
	/**
	 * 所有为null，返回true，否则返回false
	 */
	public static boolean isAllNull(Object... objects) {
		return allIsNull(objects);
	}
	public static boolean allIsNull(Object... objects) {
		for (Object object : objects) {
			if(object != null) {
				return false;
			}
		}
		return true;
	}
	/**
	 * 所有都不为null，返回true，否则返回false
	 */
	public static boolean allIsNotNull(Object... objects) {
		return !anyIsNull(objects);
	}

	/**
     * 判断str字符串中是否包含数据库操作的一些关键字，调用该方法对sql语句近判断，可避免sql注入漏洞，可能会误判
     *
     * @return 包含关键字，返回true，否则返回false
     */
    public static boolean containKeywords(String str) {
        // 对SQL语句进行安全判定
        List<String> list = Arrays.asList("insert", "update", "delete", "drop", "create", "table",
                "auto_increment", "varchar", "database", "admin", "mysql", "grant", "user");
        return list.stream().anyMatch(str.toLowerCase()::contains);
    }
    /**
     * 什么都不做
     * @param objs
     */
    public static void nothing(Object... objs) {
    	// do nothing.
    }

	/**
	 * 正则表达式来匹配可能的SQL注入攻击
	 */
	private static final String SQL_INJECTION_PATTERN = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(select|update|and|or|delete|insert|truncate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)";

	/**
	 * 正则表达式编译好的Pattern对象
	 */
	private static final Pattern PATTERN = Pattern.compile(SQL_INJECTION_PATTERN);

	/**
	 * 检测字符串是否包含SQL注入攻击的可能性
	 *
	 * @return 是否存在SQL注入风险，存在风险返回true，否则返回false
	 */
	public static boolean isSqlInjectionPossible(String str) {
		Matcher matcher = PATTERN.matcher(str);
		return matcher.find();
	}

	// 参数1为终端ID，参数2为数据中心ID
	public final static Snowflake SNOWFLAKE = IdUtil.getSnowflake(1, 1);
	/**
	 * 参数转义
	 */
	public static String escape(String origin) {
		// 如果有其他转义，继续在这里加
		if ("funcSnowflake".equals(origin)) {
			return SNOWFLAKE.nextIdStr();
		}
		return origin;
	}
    
}
